﻿/*
 * LibMediaReader.cpp
 *
 *  Created on: 2016年8月12日
 *      Author: zhengboyuan
 */

#include "BasicType.h"
#include "LibMediaReader.h"

#include "MediaReader.h"

#include "CMediaReader.h"
#include "ThreadMediaReader.h"

#include "TDiscreteArray.h"
#include "SharedPtr.h"

#include "Ffmpeg.h"
#include "FfmpegUtil.h"
#include "CLog.h"
#include "TStringCast.h"
#include "TFileUtil.h"

typedef std::shared_ptr< av::MediaReader >		MediaReaderPtr;


typedef comn::DiscreteArray< MediaReaderPtr, MAX_READER >	MediaReaderArray;

static MediaReaderArray s_readers;




int putReader(MediaReaderPtr reader)
{
	size_t idx = s_readers.put(reader);
	return (idx == -1) ? -1 : idx;
}

MediaReaderPtr getReader(int handle)
{
	if ((handle < 0) || handle >= MAX_READER)
	{
		return MediaReaderPtr();
	}

	return s_readers.getAt((size_t)handle);
}

MediaReaderPtr removeReader(int handle)
{
	MediaReaderPtr reader;
	s_readers.remove(handle, reader);
	return reader;
}


static void startup()
{
	av_register_all();
	avformat_network_init();

	CLog::info("------------ jni startup 2 ----------------\n");

}

static void cleanup()
{
	for (size_t i = 0; i < s_readers.capacity(); i++)
	{
		MediaReaderPtr ereader = s_readers.getAt(i);
		if (ereader)
		{
			ereader->close();
		}
	}

	s_readers.clear();

	avformat_network_deinit();
}


void copyTo(const av::MediaFormat& mfmt, MFormat* fmt)
{
	fmt->codec = mfmt.m_codec;
	fmt->width = mfmt.m_width;
	fmt->height = mfmt.m_height;
	fmt->framerate = mfmt.m_framerate;
	fmt->profile = mfmt.m_profile;
	fmt->clockRate = mfmt.m_clockRate;

	fmt->audioCodec = mfmt.m_audioCodec;
	fmt->channels = mfmt.m_channels;
	fmt->sampleRate = mfmt.m_sampleRate;
	fmt->audioProfile = mfmt.m_audioProfile;
	fmt->audioRate = mfmt.m_audioRate;

	if (fmt->vProp && fmt->vPropSize > 0)
	{
		int size = std::min(fmt->vPropSize, (int)mfmt.m_videoProp.size());
		memcpy(fmt->vProp, mfmt.m_videoProp.c_str(), size);
		fmt->vPropSize = size;
	}

	if (fmt->config && fmt->configSize > 0)
	{
		int size = std::min(fmt->vPropSize, (int)mfmt.m_audioConfig.size());
		memcpy(fmt->config, mfmt.m_audioConfig.c_str(), size);
		fmt->configSize = size;
	}
}


/**
 * 初始化
 * @return
 */
DLLEXPORT int mreader_init()
{
	startup();

	return 0;
}

/**
 *
 */
DLLEXPORT void mreader_quit()
{
	cleanup();
}

/**
 * 打开媒体源
 * @param handle 	返回的句柄
 * @param url		媒体源URL
 * @param params	可选参数
 * @return 0 表示成功, 其他值表示错误码
 */
DLLEXPORT int mreader_open(mreader_t* handle, const char* url, const char* params)
{
	if (handle == NULL)
	{
		return EINVAL;
	}

	if (!url || strlen(url) <= 0)
	{
		return EINVAL;
	}

	if (!params)
	{
		params = "";
	}

	CLog::info("mreader_open url:%s, params:%s\n", url, params);

	//MediaReaderPtr reader(new av::CMediaReader());
	MediaReaderPtr reader(new av::ThreadMediaReader());

	int ret = reader->open(url, params);
	if (ret == 0)
	{
		*handle = putReader(reader);
	}

	return ret;
}

/**
 * 关闭媒体源
 * @param handle
 * @return
 */
DLLEXPORT int mreader_close(mreader_t handle)
{
	CLog::info("mreader_close handle:%p\n", handle);

	MediaReaderPtr reader = removeReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	reader->close();

	return 0;
}

/**
 * 媒体源是否打开
 * @param handle
 * @return > 0 表示已经打开
 */
DLLEXPORT int mreader_isOpen(mreader_t handle)
{
	CLog::info("mreader_isOpen handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return 0;
	}

	return reader->isOpen();
}

/**
 * 获取媒体格式
 * @param handle
 * @param fmt
 * @return
 */
DLLEXPORT int mreader_getFormat(mreader_t handle, MFormat* fmt)
{
	CLog::info("mreader_getFormat handle:%p\n", handle);

	if (!fmt)
	{
		return EINVAL;
	}

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	av::MediaFormat format;
	int ret = reader->getFormat(format);

	copyTo(format, fmt);

	return ret;
}

/**
 * 获取媒体时长
 * @param handle	媒体源句柄
 * @param duration	媒体时长, 单位为毫秒
 * @return
 */
DLLEXPORT int mreader_getDuration(mreader_t handle)
{
	//CLog::info("mreader_getDuration handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	return reader->getDuration();
}

/**
 * 是否为实时媒体源
 * @param handle
 * @return > 0 表示为实时媒体源
 */
DLLEXPORT int mreader_isLive(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return 0;
	}

	return reader->isLive();
}

/**
 * 是否可以定位
 * @param handle
 * @return > 0 表示可定位
 */
DLLEXPORT int mreader_seekable(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return 0;
	}

	return reader->seekable();
}

/**
 * 播放媒体源
 * @param handle
 * @return
 */
DLLEXPORT int mreader_play(mreader_t handle)
{
	CLog::info("mreader_play handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	return reader->play();
}

/**
 * 暂停媒体源
 * @param handle
 * @return
 */
DLLEXPORT int mreader_pause(mreader_t handle)
{
	CLog::info("mreader_pause handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	return reader->pause();
}

/**
 * 停止媒体源
 * @param handle
 * @return
 */
DLLEXPORT int mreader_stop(mreader_t handle)
{
	CLog::info("mreader_stop handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	reader->stop();
	return 0;
}

/**
 * 获取媒体源状态
 * @param handle
 * @return
 */
DLLEXPORT int mreader_getState(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return av::STATE_STOPPED;
	}

	return reader->getState();
}

/**
 * 读取媒体包
 * @param handle
 * @param pkt
 * @return
 */
DLLEXPORT int mreader_read(mreader_t handle, MPacket* pkt)
{
	if (!pkt)
	{
		return EINVAL;
	}

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	av::AVPacketPtr packet;
	int ret = reader->read(packet);
	if (ret == 0)
	{
		pkt->duration = packet->duration;
		pkt->flags = packet->flags;
		pkt->pts = packet->pts;
		pkt->type = packet->stream_index;

		if (pkt->size > packet->size)
		{
			memcpy(pkt->data, packet->data, packet->size);
			pkt->size = packet->size;
		}
		else
		{
			ret = EOVERFLOW;
		}
	}
	return ret;
}

/**
 * 中断读取操作
 * @param handle
 * @return
 */
DLLEXPORT int mreader_interrupt(mreader_t handle)
{
	CLog::info("mreader_interrupt handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	reader->interrupt();
	return 0;
}


/**
 * 定位到指定偏移, 单位为微秒
 * @param handle
 * @param offset
 * @return
 */
DLLEXPORT int mreader_seek(mreader_t handle, int64_t offset)
{
	CLog::info("mreader_seek handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	int64_t pos = offset;
	return reader->seek(pos);
}

/**
 * 获取当前时间戳, 单位为微秒
 * @param handle
 * @param offset
 * @return
 */
DLLEXPORT int64_t mreader_getTime(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	int64_t offset = reader->getTime();

	return offset;
}

/**
 * 开始记录为文件
 * @param handle
 * @param filename	录像文件
 * @return
 */
DLLEXPORT int mreader_startRecord(mreader_t handle, const char* filename)
{
	if (!filename || strlen(filename) <= 0)
	{
		return EINVAL;
	}

	CLog::info("mreader_startRecord handle:%p, filename:%s\n", handle, filename);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	return reader->startRecord(filename);
}

/**
 * 停止记录
 * @param handle
 * @return
 */
DLLEXPORT int mreader_stopRecord(mreader_t handle)
{
	CLog::info("mreader_stopRecord handle:%p\n", handle);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	reader->stopRecord();
	return 0;
}

/**
 * 是否正在记录
 * @param handle
 * @return > 0 表示正在记录
 */
DLLEXPORT int mreader_isRecording(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return 0;
	}

	return reader->isRecording();
}

DLLEXPORT int mreader_setEventCallback(mreader_t handle, MReaderEventCallback cb, void* context)
{
	CLog::info("mreader_setEventCallback handle:%d, cb:%p, context:%p\n", handle, cb, context);

	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENODEV;
	}

	reader->setEventCallback(cb, context);
	return 0;
}

DLLEXPORT void mreader_enableLog(int enabled)
{
	if (enabled > 0)
	{
		CLog::setLogger(CLog::COUT);
#ifdef WIN32
		CLog::setLogger(CLog::DEBUGWINDOW, CLog::kNone, 0);
#endif //
	}
	else
	{
		CLog::resetLogger();
	}
}

DLLEXPORT int mreader_wait(mreader_t handle, int ms)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		av_usleep(1000);
		return ENODEV;
	}

	return reader->waitForReadable(ms);
}

DLLEXPORT int mreader_setScale(mreader_t handle, float scale)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	return reader->setScale(scale);
}

DLLEXPORT float mreader_getScale(mreader_t handle)
{
	float scale = 1.0f;
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return scale;
	}

	scale = reader->getScale();

	return scale;
}

DLLEXPORT int mreader_slow(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	float scale = reader->getScale();
	scale -= MREADER_STEP_SCALE;
	if (scale < MREADER_MIN_SCALE)
	{
		scale = MREADER_MIN_SCALE;
	}

	return reader->setScale(scale);
}

DLLEXPORT int mreader_fast(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	float scale = reader->getScale();
	scale += MREADER_STEP_SCALE;
	if (scale > MREADER_MAX_SCALE)
	{
		scale = MREADER_MAX_SCALE;
	}

	return reader->setScale(scale);
}

DLLEXPORT int mreader_step(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	return reader->step();
}

DLLEXPORT int mreader_nextKeyFrame(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	return reader->nextKeyFrame();
}

DLLEXPORT void mreader_setStackSize(int bytes)
{
	av::ThreadMediaReader::STACK_SIZE = bytes;
}

DLLEXPORT int mreader_backward(mreader_t handle, int64_t offset)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	return reader->backward(offset);
}

DLLEXPORT int mreader_forward(mreader_t handle)
{
	MediaReaderPtr reader = getReader(handle);
	if (!reader)
	{
		return ENOENT;
	}

	return reader->forward();
}

DLLEXPORT void mreader_setStreamBreakTimeout(int ms)
{
    av::ThreadMediaReader::STREAM_BREAK_TIMEOUT = ms;
}

DLLEXPORT void mreader_setMaxQueueSize(size_t count)
{
    if (count > 8)
    {
        av::ThreadMediaReader::MAX_QUEUE_SIZE = count;
    }
}